import { Slots } from 'vue';
import { isFunction } from '/@/utils/is';
import { RenderOpts } from '/@/components/Form';

/**
 * 获取并调用指定插槽的函数。
 *
 * 此函数检查插槽对象中是否存在给定的插槽键，并且该键对应的值是函数，如果是则调用它并返回结果。
 *
 * @param {Slots} slots - 包含多个插槽函数的对象。
 * @param {string} [slot='default'] - 要获取的插槽的名称，默认为'default'。
 * @param {any} [data] - 传递给插槽函数的额外数据。
 * @param {RenderOpts} [opts] - 传递给插槽函数的渲染选项。
 *
 * @returns {any|null} 如果插槽函数存在且可以调用，则返回插槽函数执行后的结果。
 *                     如果插槽函数不存在或插槽对应的不是函数，则返回null。
 */
export function getSlot(slots: Slots, slot = 'default', data?: any, opts?: RenderOpts) {
  // 检查插槽对象或插槽对应的键是否存在。
  if (!slots || !Reflect.has(slots, slot)) {
    return null;
  }

  // 检查对应键的值是否是函数。
  if (!isFunction(slots[slot])) {
    console.error(`${slot} is not a function!`);
    return null;
  }

  // 获取插槽函数。
  const slotFn = slots[slot];

  // 如果没有对应的插槽函数，返回null。
  if (!slotFn) return null;

  // 合并传递给插槽函数的参数。
  const params = { ...data, ...opts };

  // 调用插槽函数，并传入合并后的参数，返回结果。
  return slotFn(params);
}

/**
 * 扩展插槽函数。
 *
 * 此函数允许您从现有插槽对象中创建一个新的插槽函数对象，您可以选择排除某些键。
 *
 * @param {Slots} slots - 一个包含不同插槽的对象。
 * @param {string[]} [excludeKeys=[]] - 要从结果中排除的插槽键列表。
 * @returns {any} 返回一个新的对象，该对象包含除排除键之外的所有原始插槽键，
 *                以及用于生成该插槽内容的新函数。
 */
export function extendSlots(slots: Slots, excludeKeys: string[] = []) {
  // 获取插槽的键名。
  const slotKeys = Object.keys(slots);
  // 准备一个用于返回的新对象。
  const ret: any = {};

  // 遍历所有插槽键。
  slotKeys.map((key) => {
    // 如果当前键不在排除列表中，则处理它。
    if (!excludeKeys.includes(key)) {
      // 为每个插槽键创建一个新的函数，该函数可用于获取插槽内容。
      ret[key] = (data?: any) => getSlot(slots, key, data);
    }
  });

  // 返回包含扩展插槽函数的新对象。
  return ret;
}
